﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data.Objects.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.ArchitectureTools.CodeRepositoryQuery;
using UCL.Powersource.Analysis;


namespace UCL.Powersource.Statistics
{
    /// <summary>
    /// Number of subclasses in the code index assembly calculation class
    /// </summary>
    [Export(typeof(IStatistic))]
    [DataContract(IsReference = true)]
    public class DepthOfInheritance : StatisticTeamBase
    {
        public static readonly Guid CN_Statistic_Guid = new Guid("9C2ACA4D-D60D-4B65-A26F-E385759682CD");
        [ExcludeFromCodeCoverage]
        public override Guid Statistic_Guid
        {
            get { return CN_Statistic_Guid; }
        }

        public DepthOfInheritance()
        {
            ClassWithMaxDOI = "";
        }


        [ExcludeFromCodeCoverage]
        public override string Label
        {
            get { return "Depth of Inheritance"; }
        }

        [ExcludeFromCodeCoverage]
        public override string Description
        {
            get { return "Depth of inheritance in the code index assemblyFile"; }
        }

        [ExcludeFromCodeCoverage]
        public override string Name
        {
            get { return "DepthOfInheritance"; }
        }

        [DataMember]
        private string _FormatString = "0";

        public override string FormatString
        {
            get
            {
                return _FormatString;
            }
        }

 

        /// <summary>
        /// Class for number of subclasses in the code index assembly calculation
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project configuration</param>
        /// <param name="timeFrame">Work item iteration time frame</param>
        /// <returns>The number of subclasses in the code index assembly</returns>
        protected override double? CalculateFor(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame)
        {
            int depthOfInheritance = 0;
            IQueryable<TypeDefinition> type_definitions;

            var analysisBridge = teamProjConfig.ProjectAnalysisBridge;
            var codeIndex = analysisBridge.RetrieveCodeIndex(timeFrame); 

            // We won't modify the code index.
            codeIndex.Assemblies.MergeOption =          System.Data.Objects.MergeOption.NoTracking;
            codeIndex.AssemblyReferences.MergeOption =  System.Data.Objects.MergeOption.NoTracking;
            codeIndex.Namespaces.MergeOption =          System.Data.Objects.MergeOption.NoTracking;
            codeIndex.Types.MergeOption =               System.Data.Objects.MergeOption.NoTracking;
            codeIndex.TypeDefinitions.MergeOption =     System.Data.Objects.MergeOption.NoTracking;
            codeIndex.MethodDefinitions.MergeOption =   System.Data.Objects.MergeOption.NoTracking;
            codeIndex.Fields.MergeOption =              System.Data.Objects.MergeOption.NoTracking;
            codeIndex.Properties.MergeOption =          System.Data.Objects.MergeOption.NoTracking;
            codeIndex.Events.MergeOption =              System.Data.Objects.MergeOption.NoTracking;

            // Get a collection with the type definitions of those classes in the
            // assemblyFile that do not inherit from the System.Object class or any other 
            // defined inside the System namespace. 
            type_definitions =
                (from td in codeIndex.TypeDefinitions
                where td.KindAsShort == (short)TypeKind.Class
                let baseType = td.BaseTypeSignature.Type
                where baseType != null 
                    && !(baseType.Hash_Name == SqlFunctions.Checksum("Object") 
                    && baseType.Namespace.Hash_Name == SqlFunctions.Checksum("System"))
                select td);

            int depth_level = 0;

            foreach (TypeDefinition td in type_definitions)
            {
                depth_level =
                    getRecursiveInheritanceDepth(type_definitions, td.Type.Name);

                if (depth_level > depthOfInheritance)
                {
                    depthOfInheritance = depth_level;
                    ClassWithMaxDOI = td.Type.Name;
                }
            }

            return depthOfInheritance;
        }

        /// <summary>
        /// This is a recursive method that evaluates the name of the parent 
        /// class the child class whose name is received as a parameter subclasses 
        /// from. If the parent class is not Object and it is found in the type
        /// definitions context, then the parent class' is send as an argument to
        /// the recursive method, this time as a child. At the end, we find the
        /// number of levels in which the original class is nested.
        /// </summary>
        /// <param name="type_definitions">Types of the definition</param>
        /// <param name="name">Name of the definition</param>
        /// <returns></returns>
        private static int getRecursiveInheritanceDepth(IQueryable<TypeDefinition> type_definitions, string name)
        {
            string base_type_name = "";
            TypeDefinition type_def = null;

            foreach (TypeDefinition td in type_definitions)
            {
                if (td.Type.Name == name)
                {
                    type_def = td;
                    base_type_name = type_def.BaseTypeSignature.Type.Name;
                    break;
                }
            }

            if (type_def == null || base_type_name == "Object")
                return 0;
            else
                return 1 + getRecursiveInheritanceDepth(type_definitions, base_type_name);
        }

      
        private string ClassWithMaxDOI
        {
            set; get;
        }

        public override bool RequiresBuild
        {
            get { return true; }
        }
    }
}
